package com.tiancheng.trade.order.feign;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.tiancheng.trade.commom.web.http.PlatformRequest;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.order.bo.ums.BindingBankCardBO;
import com.tiancheng.trade.order.bo.ums.UmsBaseBO;
import com.tiancheng.trade.order.enums.UmsTradeTypeEnum;
import com.tiancheng.trade.order.model.BankCard;
import com.tiancheng.trade.order.util.ChineseCharacterUtil;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.order.util.ums.SecureUtil;
import com.tiancheng.trade.order.util.ums.UmsAcpService;
import com.tiancheng.trade.order.util.ums.UmsBindingBankCardConfig;
import com.tiancheng.trade.order.util.ums.UmsConstant;
import com.tiancheng.trade.order.vo.app.BankCardBindingVo;
import com.tiancheng.trade.order.vo.app.res.BankInfoVo;
import com.tiancheng.trade.order.vo.open.OpenUmsAccStatusQueryRes;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: likailun
 * @Description: 银联请求处理
 * @Date: create in 2021/8/24 11:05 上午
 */
@Slf4j
@Component
public class UmsService {
    @Resource
    private PlatformRequest platformRequest;
    @Resource
    private UmsBindingBankCardConfig umsConfig;


    private static final Map<String,String> BANK_BACKGROUND_IMAGE_MAP=new HashMap<String,String>(){{
        put("01050000","https://app-1254950508.cos.ap-guangzhou.myqcloud.com/fwpt-mid-trade/01050000.png");// 建设银行
        put("01040000","https://app-1254950508.cos.ap-guangzhou.myqcloud.com/fwpt-mid-trade/01040000.png");// 中国银行
        put("01020000","https://app-1254950508.cos.ap-guangzhou.myqcloud.com/fwpt-mid-trade/01020000.png");// 工商银行
        put("03080000","https://app-1254950508.cos.ap-guangzhou.myqcloud.com/fwpt-mid-trade/03080000.png");// 招商银行
        put("01030000","https://app-1254950508.cos.ap-guangzhou.myqcloud.com/fwpt-mid-trade/01030000.png");// 农业银行
        put("other","https://app-1254950508.cos.ap-guangzhou.myqcloud.com/fwpt-mid-trade/other_bank.png");// 其他银行银行
    }};

    /**
     * 查询开卡状态-token支付版本<br/>
     * 如果flowId不为空则使用flowId查询，否则根据accNo查询<br/>
     * @param flowId 开卡订单号
     * @param txnTime flow createTime 流水创建时间，根据accNo查询时传入当前时间
     * @return
     */
    public OpenUmsAccStatusQueryRes queryOpenInfo(@NotNull String flowId, @NotNull LocalDateTime txnTime, @NotNull String sn) {
        final BindingBankCardBO bindingBankCardBO = new BindingBankCardBO();
        bindingBankCardBO.setVersion(UmsConstant.VERSION);
        bindingBankCardBO.setReqType(UmsTradeTypeEnum.BINDING_CAR_QUERY_REQ.getCode());
        bindingBankCardBO.setIssCode(umsConfig.getIssCode());
        bindingBankCardBO.setSignType(umsConfig.getSignMethod());

        bindingBankCardBO.setOrderNo(flowId);
        bindingBankCardBO.setOrderTime(DateTimeUtil.format(txnTime,"yyyyMMddhhmmss"));
        bindingBankCardBO.setSn(sn);

        Map<String, String> paramsMap = JSON.parseObject(JSON.toJSONString(bindingBankCardBO, SerializerFeature.WriteMapNullValue), Map.class);
        Map<String, String> reqData = UmsAcpService.sign(paramsMap,umsConfig);			  //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。

        log.info("查询绑卡信息，params：{}", JSON.toJSONString(reqData));
		String resultString = platformRequest.getHttpRequestUtils().post(umsConfig.getUmsServerUrl()).addBody(reqData).execute(String.class);
		Map<String, String> rspData = null;
        final OpenUmsAccStatusQueryRes result = new OpenUmsAccStatusQueryRes();
        try {
            rspData = UmsAcpService.convertResultStringToMap(resultString);
            String respCode = rspData.get("respCode") ;
            if(("00").equals(respCode)){
                return openCardMsgMap2Entity(rspData);
            }else{
                //其他应答码为失败请排查原因
                log.info("查询银联绑卡错误");
            }
        } catch (Exception e) {
            log.error("查询卡状态失败:",e);
        }

        return result;
    };


    /**
     * 查询信息解密并转换
     * @param msgMap
     * @return
     */
    public OpenUmsAccStatusQueryRes openCardMsgMap2Entity(Map<String, String> msgMap){
        Map<String, Object> result = new HashMap<>();
        if(msgMap!=null && !msgMap.isEmpty()){
            if(UmsAcpService.validate(msgMap, umsConfig)){
                log.info("验证签名成功");
                String respCode = msgMap.get("respCode") ;
                result.putAll(msgMap);
                if(("00").equals(respCode)){
                    String status = msgMap.get("status");
                    String bindCardInfo = msgMap.get("bindCardInfo");
                    if(StringUtils.isNotEmpty(bindCardInfo)){
                        // 有敏感信息加密时这么解：
                        Map<String, String> bindCardInfoMap = UmsAcpService.parsePayeeInfoEnc(bindCardInfo, UmsConstant.ENCODING);
                        result.putAll(bindCardInfoMap);
                    }
                }else{
                    //其他应答码为失败请排查原因或做失败处理
                    log.error("查询开卡信息失败：{}",respCode);
                }
            }else{
                log.error("验证签名失败");
                //TODO 检查验证签名失败的原因
            }
        }else{
            //未返回正确的http状态
            log.error("未获取到返回报文或返回http状态码非200");
        }
        OpenUmsAccStatusQueryRes openUmsAccStatusQueryRes=null;
        if(result.size()>0){
            openUmsAccStatusQueryRes= JSON.parseObject(JSON.toJSONString(result, SerializerFeature.WriteMapNullValue), OpenUmsAccStatusQueryRes.class);
        }
        return openUmsAccStatusQueryRes;
    }

    /**
     * 查询银行卡列表
     * @param orderNo 订单号
     * @return 银行卡列表
     */
    public List<BankInfoVo> queryBankList(String orderNo){
        List<BankInfoVo> list=new ArrayList<BankInfoVo>();
        final UmsBaseBO umsBaseReqDTO = new UmsBaseBO();
        umsBaseReqDTO.setVersion(UmsConstant.VERSION);
        umsBaseReqDTO.setReqType(UmsTradeTypeEnum.QUERY_BANK_LIST.getCode());
        umsBaseReqDTO.setIssCode(umsConfig.getIssCode());
        umsBaseReqDTO.setSignType(umsConfig.getSignMethod());
        umsBaseReqDTO.setOrderNo(orderNo);
        umsBaseReqDTO.setOrderTime(DateTimeUtil.format(LocalDateTime.now(),"yyyyMMddhhmmss"));

        Map<String, String> paramsMap = JSON.parseObject(JSON.toJSONString(umsBaseReqDTO, SerializerFeature.WriteMapNullValue), Map.class);
        Map<String, String> reqData = UmsAcpService.sign(paramsMap,umsConfig);			  //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        log.info("查询银行卡列表，params：{}", JSON.toJSONString(reqData));
		String resultString = platformRequest.getHttpRequestUtils().post(umsConfig.getUmsServerUrl()).addBody(reqData).execute(String.class);
        Map<String, String> rspData = null;
        try {
            rspData = UmsAcpService.convertResultStringToMap(resultString);
            if(UmsAcpService.validate(rspData,umsConfig)){
                log.info("获取到银行卡列表，银联返回：{}", JsonUtil.logToJson(rspData));
                String respCode = rspData.get("respCode") ;
                if(("00").equals(respCode)){
                    String bindBankList = rspData.get("bindBankList") ;
                    if(StringUtils.isEmpty(bindBankList)){
                        return list;
                    }
                    //解base64
                    bindBankList=new String(SecureUtil.base64Decode(bindBankList.getBytes(UmsConstant.ENCODING)),UmsConstant.ENCODING);
                    log.info("获取到银行卡列表：{}",bindBankList);
                    final List<Map> maps = JSON.parseArray(bindBankList, Map.class);
                    if(maps==null || maps.size()<1){
                        return list;
                    }
                    maps.stream().collect(Collectors.groupingBy(map -> map.get("bankCode").toString())).forEach((s, group) -> {
                        final List<BankInfoVo.AccTypeItem> accTypeItems = new ArrayList<>();
                        for (Map map : group) {
                            accTypeItems.add(new BankInfoVo.AccTypeItem(MapUtils.getString(map, "cloudPayCardAttr"), MapUtils.getString(map, "bindBankUrl")));
                        }
                        final String bankName = MapUtils.getString(group.get(0), "bankName");

                        list.add(new BankInfoVo(
                                bankName,
                                MapUtils.getString(group.get(0),"bankCode"),
                                accTypeItems,
                                MapUtils.getString(group.get(0),"bankLogoUrl"),
                                StringUtils.isNotEmpty(bankName)?ChineseCharacterUtil.getUpperCase(bankName.substring(0,1),false):"",
                                getBackgroundImg(MapUtils.getString(group.get(0),"bankCode"))
                        ));
                    });
                    return list;
                }else{
                    //其他应答码为失败请排查原因
                    log.error("获取到银行卡列表，银联返回失败");
                    BusinessErrorInfoEnum.BANK_CARD_QUERY_BANK_LIST_ERROR.assertFail();
                }
            }
            BusinessErrorInfoEnum.BANK_CARD_QUERY_BANK_LIST_ERROR.assertFail();
        } catch (IOException e) {
            log.error("查询卡状态失败:",e);
        }
        return list;
    }

    public String getBackgroundImg(String bankCode){
        return MapUtils.getString(BANK_BACKGROUND_IMAGE_MAP,bankCode,MapUtils.getString(BANK_BACKGROUND_IMAGE_MAP,"other"));
    }

    /**
     * 查询银行卡列表
     * @param bindingBankCardBO 订单基础信息
     * @param params  用户证件信息等
     * @return 银行卡列表
     */
    public String bindingCard(BindingBankCardBO bindingBankCardBO, BankCardBindingVo params){
        bindingBankCardBO.setVersion(UmsConstant.VERSION);
        bindingBankCardBO.setReqType(UmsTradeTypeEnum.BINDING_CARD_REQ.getCode());
        bindingBankCardBO.setIssCode(umsConfig.getIssCode());
        bindingBankCardBO.setSignType(umsConfig.getSignMethod());

        //  不能传卡号，如果传的话会报错
        Map<String, String> contentData = JSON.parseObject(JSON.toJSONString(bindingBankCardBO, SerializerFeature.WriteMapNullValue), Map.class);
        // 填充绑卡信息
        Map<String, String> bindCardInfoMap = new HashMap<String, String>();
        bindCardInfoMap.put("bankCode", params.getBankCode());
        bindCardInfoMap.put("cloudPayCardAttr", params.getAccType());
        bindCardInfoMap.put("name", params.getName());// 目前不校验
        bindCardInfoMap.put("certifTp", params.getCertifTp());
        bindCardInfoMap.put("certifId", params.getCertifId());
        bindCardInfoMap.put("mobile", params.getMobile());
        bindCardInfoMap.put("appUserId", params.getUid());

        contentData.put("bindCardInfo", UmsAcpService.getPayeeInfoWithEncrpyt(bindCardInfoMap,UmsConstant.ENCODING));
        contentData.put("encryptCertId", UmsAcpService.getEncryptCertId());

        // token模式必填，参数同银联确认
        contentData.put("tokenExpr", umsConfig.getTokenExpr());
        contentData.put("tranLmt", umsConfig.getTranLmt());
        contentData.put("useNo", umsConfig.getUseNo());

        final Map<String, String> afterSignParam = UmsAcpService.sign(contentData, umsConfig);
        BusinessErrorInfoEnum.BANK_CARD_BINDING_ERROR.assertIsTrue(afterSignParam != null);
        log.info("银联一键绑卡申请，params：{}", JSON.toJSONString(afterSignParam));
		String resultString = platformRequest.getHttpRequestUtils().post(umsConfig.getUmsServerUrl()).addBody(afterSignParam).execute(String.class);
        Map<String, String> rspData = null;
        try {
            rspData = UmsAcpService.convertResultStringToMap(resultString);
            log.info("银联一键绑卡申请，银联返回：{}", rspData);
            String respCode = rspData.get("respCode") ;
            if(("00").equals(respCode)){
                return MapUtils.getString(rspData,"sn");

            }
        } catch (Exception e) {
            log.error("银联一键绑卡申请，异常:",e);
        }
        return null;
    }

    /**
     * 申请银行卡解除绑定
     * @param bankCard  绑卡信息
     * @param unboundOrderNo  解绑订单id
     * @return 银行卡列表
     */
    public boolean unboundCard(BankCard bankCard,String unboundOrderNo){
        Map<String, String> contentData = new HashMap<String,String>();
        contentData.put("version", UmsConstant.VERSION);
        contentData.put("reqType", UmsTradeTypeEnum.UNBOUND_CARD_REQ.getCode());
        contentData.put("issCode", umsConfig.getIssCode());
        contentData.put("signType", umsConfig.getSignMethod());
        contentData.put("orderNo",unboundOrderNo);
        contentData.put("orderTime",DateTimeUtil.format(LocalDateTime.now(),"yyyyMMddhhmmss"));
        // 填充绑卡信息
        Map<String, String> bindCardInfoMap = new HashMap<String, String>();
        bindCardInfoMap.put("bankCode", bankCard.getBankCode());
        bindCardInfoMap.put("cloudPayCardAttr", bankCard.getAccType());
        bindCardInfoMap.put("name", bankCard.getCustomerName());// 目前不校验
        bindCardInfoMap.put("certifTp", bankCard.getCertifTp());
        bindCardInfoMap.put("certifId", bankCard.getCertifId());
        bindCardInfoMap.put("mobile", bankCard.getMobile());
        bindCardInfoMap.put("appUserId", bankCard.getUid());
        bindCardInfoMap.put("cloudPayUserId", bankCard.getUnionpayUserId());
        bindCardInfoMap.put("accNo",bankCard.getToken());

        contentData.put("bindCardInfo", UmsAcpService.getPayeeInfoWithEncrpyt(bindCardInfoMap,"UTF-8"));
        contentData.put("encryptCertId", UmsAcpService.getEncryptCertId());

        // token模式必填，参数同银联确认
        contentData.put("tokenExpr", umsConfig.getTokenExpr());
        contentData.put("tranLmt", umsConfig.getTranLmt());
        contentData.put("useNo", umsConfig.getUseNo());

        final Map<String, String> afterSignParam = UmsAcpService.sign(contentData, umsConfig);
        BusinessErrorInfoEnum.BANK_CARD_BINDING_ERROR.assertIsTrue(afterSignParam != null);
        log.info("银联解绑绑申请，params：{}", JSON.toJSONString(afterSignParam));
		String resultString = platformRequest.getHttpRequestUtils().post(umsConfig.getUmsServerUrl()).addBody(afterSignParam).execute(String.class);
        Map<String, String> rspData = null;
        try {
            rspData = UmsAcpService.convertResultStringToMap(resultString);
            log.info("银联解绑绑申请，银联返回：{}", rspData);
            String respCode = rspData.get("respCode") ;
            if(("00").equals(respCode)){
                return true;

            }
        } catch (Exception e) {
            log.error("银联解绑绑申请，异常:",e);
        }
        return false;
    }


}
